package com.baidu.iit.pxp;

import com.baidu.iit.pvm.ActivityException;
import com.baidu.iit.pvm.runtime.ProcessInstance;
import com.baidu.iit.pxp.el.ExpressionManager;
import com.baidu.iit.pxp.entity.ProcessDefinitionEntity;
import com.baidu.iit.pxp.handler.*;
import com.baidu.iit.pxp.parser.BpmnParse;
import com.baidu.iit.pxp.parser.BpmnParseHandler;
import com.baidu.iit.pxp.parser.BpmnParseHandlers;
import com.baidu.iit.pxp.parser.BpmnParser;
import com.baidu.iit.pxp.parser.factory.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * User: huangweili
 * Date: 14-4-27
 * Time: 上午8:50
 */
public final class PxpServiceManager {


    private static Logger logger = LoggerFactory.getLogger(PxpServiceManager.class);

    public List<BpmnParseHandler> customDefaultBpmnParseHandlers;

    private static ExpressionManager expressionManager = new ExpressionManager();

    private BpmnParseFactory parseFactory=new DefaultBpmnParseFactory();

    private ActivityBehaviorFactory behaviorFactory=new DefaultActivityBehaviorFactory();

    private ListenerFactory listenerFactory=new DefaultListenerFactory();

    private BpmnParser parser;

    private BpmnParseHandlers parseHandlers;


    private AtomicBoolean inited = new AtomicBoolean(false);


    private void inintParseFactory() {
        //初始化构建工厂
        if (inited.compareAndSet(false, true)) {
            parseHandlers = new BpmnParseHandlers();
            parseHandlers.addHandlers(getDefaultBpmnParseHandlers());
            parser = new BpmnParser();
            parser.setExpressionManager(expressionManager);
            parser.setBpmnParseFactory(parseFactory);
            parser.setBpmnParserHandlers(parseHandlers);
            parser.setActivityBehaviorFactory(behaviorFactory);
            parser.setListenerFactory(listenerFactory);
        }
    }


    public List<ProcessInstance> createProcessInstance(InputStream inputStream) {
        inintParseFactory();
        BpmnParse parse = parseFactory.createBpmnParse(parser);
        parse.sourceInputStream(inputStream);
        parse.execute();
        List<ProcessDefinitionEntity> processDefinitions = parse.getProcessDefinitions();
        List<ProcessInstance> processInstances = new ArrayList<ProcessInstance>();
        if (processDefinitions != null && processDefinitions.size() > 0) {
            for (ProcessDefinitionEntity entity : processDefinitions) {
                processInstances.add(entity.createProcessInstance());
            }
        }
        return processInstances;

    }

    public List<ProcessInstance> createProcessInstance(String xmlPath) throws FileNotFoundException {

        return createProcessInstance(new FileInputStream(new File(xmlPath)));
    }


    protected List<BpmnParseHandler> getDefaultBpmnParseHandlers() {

        List<BpmnParseHandler> bpmnParserHandlers = new ArrayList<BpmnParseHandler>();


        //bpmnParserHandlers.add(new BoundaryEventParseHandler());
        //bpmnParserHandlers.add(new CallActivityParseHandler());
        //bpmnParserHandlers.add(new CancelEventDefinitionParseHandler());
        //bpmnParserHandlers.add(new CompensateEventDefinitionParseHandler());
        //bpmnParserHandlers.add(new ErrorEventDefinitionParseHandler());
        //bpmnParserHandlers.add(new EventBasedGatewayParseHandler());
        //bpmnParserHandlers.add(new IntermediateCatchEventParseHandler());
        //bpmnParserHandlers.add(new IntermediateThrowEventParseHandler());
        //bpmnParserHandlers.add(new MessageEventDefinitionParseHandler());

        bpmnParserHandlers.add(new EndEventParseHandler());

        bpmnParserHandlers.add(new ExclusiveGatewayParseHandler());

        bpmnParserHandlers.add(new InclusiveGatewayParseHandler());

        bpmnParserHandlers.add(new ParallelGatewayParseHandler());
        bpmnParserHandlers.add(new ServiceTaskParseHandler());
        bpmnParserHandlers.add(new ProcessParseHandler());

        bpmnParserHandlers.add(new SequenceFlowParseHandler());

        bpmnParserHandlers.add(new StartEventParseHandler());

        bpmnParserHandlers.add(new SubProcessParseHandler());

        bpmnParserHandlers.add(new EventSubProcessParseHandler());

        bpmnParserHandlers.add(new TaskParseHandler());

        bpmnParserHandlers.add(new TransactionParseHandler());


        if (customDefaultBpmnParseHandlers != null) {
            Map<Class<?>, BpmnParseHandler> customParseHandlerMap = new HashMap<Class<?>, BpmnParseHandler>();
            for (BpmnParseHandler bpmnParseHandler : customDefaultBpmnParseHandlers) {
                for (Class<?> handledType : bpmnParseHandler.getHandledTypes()) {
                    customParseHandlerMap.put(handledType, bpmnParseHandler);
                }
            }

            for (int i = 0; i < bpmnParserHandlers.size(); i++) {
                BpmnParseHandler defaultBpmnParseHandler = bpmnParserHandlers.get(i);
                if (defaultBpmnParseHandler.getHandledTypes().size() != 1) {
                    StringBuilder supportedTypes = new StringBuilder();
                    for (Class<?> type : defaultBpmnParseHandler.getHandledTypes()) {
                        supportedTypes.append(" " + type.getCanonicalName() + " ");
                    }
                    throw new ActivityException("The default BPMN parse handlers should only support one type, but " + defaultBpmnParseHandler.getClass()
                            + " supports " + supportedTypes.toString() + ". This is likely a programmatic error");
                } else {
                    Class<?> handledType = defaultBpmnParseHandler.getHandledTypes().iterator().next();
                    if (customParseHandlerMap.containsKey(handledType)) {
                        BpmnParseHandler newBpmnParseHandler = customParseHandlerMap.get(handledType);
                        if (logger.isInfoEnabled()) {
                            logger.info("Replacing default BpmnParseHandler " + defaultBpmnParseHandler.getClass().getName() + " with " + newBpmnParseHandler.getClass().getName());
                        }
                        bpmnParserHandlers.set(i, newBpmnParseHandler);
                    }
                }
            }
        }

        return bpmnParserHandlers;
    }

    public List<BpmnParseHandler> getCustomDefaultBpmnParseHandlers() {
        return customDefaultBpmnParseHandlers;
    }

    public void setCustomDefaultBpmnParseHandlers(List<BpmnParseHandler> customDefaultBpmnParseHandlers) {
        this.customDefaultBpmnParseHandlers = customDefaultBpmnParseHandlers;
    }

    public static ExpressionManager getExpressionManager() {
        return expressionManager;
    }
}
